package com.nnnu.orange.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nnnu.orange.constant.MessageConstants;
import com.nnnu.orange.constant.StatusConstants;
import com.nnnu.orange.exception.ObjectException;
import com.nnnu.orange.mapper.BooksMapper;
import com.nnnu.orange.mapper.UsersMapper;
import com.nnnu.orange.pojo.dto.BorrowingDto;
import com.nnnu.orange.pojo.dto.BorrowingQueryDto;
import com.nnnu.orange.pojo.dto.BorrowingQueryDto2;
import com.nnnu.orange.pojo.dto.ReturnBookDto;
import com.nnnu.orange.pojo.entity.Books;
import com.nnnu.orange.pojo.entity.Borrowings;
import com.nnnu.orange.mapper.BorrowingsMapper;
import com.nnnu.orange.pojo.entity.Users;
import com.nnnu.orange.pojo.vo.BorrowingVO;
import com.nnnu.orange.result.PageResult;
import com.nnnu.orange.service.IBorrowingsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nnnu.orange.utils.ThreadLocalUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;

/**
 * <p>
 * 记录用户借阅图书的信息 服务实现类
 * </p>
 *
 * @author zk
 * @since 2024-11-07
 */
@Service
@RequiredArgsConstructor
public class BorrowingsServiceImpl extends ServiceImpl<BorrowingsMapper, Borrowings> implements IBorrowingsService {

    private final UsersMapper userMapper;
    private final BooksMapper bookMapper;

    /**
     * 分页查询借阅记录数据
     *
     * @param queryDto dto
     * @return 分页后的数据
     */
    @Override
    public PageResult<?> pageList(@NotNull BorrowingQueryDto2 queryDto) {
        LocalDate returnDate = queryDto.getReturnDate();
        LocalDate dueDate = queryDto.getDueDate();
        LocalDate borrowDate = queryDto.getBorrowDate();

        BorrowingQueryDto dto = BorrowingQueryDto.builder()
                .pageNum(queryDto.getPageNum())
                .pageSize(queryDto.getPageSize())
                .bookId(queryDto.getBookId())
                .status(queryDto.getStatus())
                .borrowDateStart(convertToStartOfDay(borrowDate))
                .borrowDateEnd(convertToEndOfDay(borrowDate))
                .dueDateStart(convertToStartOfDay(dueDate))
                .dueDateEnd(convertToEndOfDay(dueDate)).returnDateStart(convertToStartOfDay(returnDate)).returnDateEnd(convertToEndOfDay(returnDate)).build();

        Page<?> borrowingVOPage = baseMapper.selectPageList(new Page<>(dto.getPageNum(), dto.getPageSize()), dto);

        return new PageResult<>(borrowingVOPage.getTotal(), borrowingVOPage.getRecords());
    }

    /**
     * 管理员获取借阅详细信息
     *
     * @param borrowingId 借阅唯一id
     * @return 借阅实体
     */
    @Override
    public BorrowingVO findById(Integer borrowingId) {
        return baseMapper.selectByBorrowingId(borrowingId);
    }


    /**
     * 批量删除借阅记录
     *
     * @param ids id列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(@NotNull List<Integer> ids) {

        List<Integer> borrowingIds = ids.stream().distinct().toList();


        List<Borrowings> borrowings = baseMapper.selectBatchIds(borrowingIds);


        boolean hasInvalidStatus = borrowings.stream().anyMatch(borrowing -> borrowing.getStatus() == Borrowings.STATUS_CONFIRMED || borrowing.getStatus() == Borrowings.STATUS_CANCELLED);

        if (hasInvalidStatus) {
            throw new ObjectException("存在借阅中或逾期的记录，无法删除");
        }


        int deleteCount = baseMapper.deleteBatchIds(borrowingIds);
        if (deleteCount < borrowingIds.size()) {
            throw new ObjectException("部分记录删除失败，请重试");
        }
    }

    /**
     * 用户借阅图书
     *
     * @param borrowingDto dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userBorrowing(@NotNull BorrowingDto borrowingDto) {
        Long userId = ThreadLocalUtil.getId();
        Integer bookId = borrowingDto.getBookId();
        Users user = userMapper.selectById(userId);
        if (user == null) throw new ObjectException(MessageConstants.USER_NOT_FOUND);
        Books book = bookMapper.selectById(bookId);
        if (book == null || book.getAvailableCopies() <= 0)
            throw new ObjectException(MessageConstants.RESERVATION_BOOK_ERR);
        List<Borrowings> userBorrowings = baseMapper.selectList(new LambdaQueryWrapper<Borrowings>()
                .eq(Borrowings::getUserId, userId)
                .eq(Borrowings::getStatus, StatusConstants.ONE)); // 借阅中
        boolean hasOverdue = userBorrowings.stream().anyMatch(borrowing -> borrowing.getDueDate().isBefore(LocalDateTime.now()) && borrowing.getReturnDate() == null);
        if (hasOverdue) {
            throw new ObjectException("存在逾期未归还的书籍，无法借阅");
        }
        boolean s = userBorrowings.stream().anyMatch(borrowing -> borrowing.getBookId().equals(bookId));
        if (s) {
            throw new ObjectException("您已借阅该书，无法重复借阅");
        }
        int maxBorrowLimit = user.getMembershipType() == 1 ? 5 : 3;
        if (userBorrowings.size() >= maxBorrowLimit) {
            throw new ObjectException("借阅数量已达上限，无法借阅");
        }
        Borrowings borrowing = new Borrowings();
        borrowing.setUserId(Math.toIntExact(userId)).setBookId(bookId).setBorrowDate(LocalDateTime.now()).setStatus(StatusConstants.ONE)
                .setDueDate(LocalDateTime.now().plusDays(15).withHour(19).withMinute(0).withSecond(0))
                .setNotes(StringUtils.isNotBlank(borrowingDto.getNotes()) ? borrowingDto.getNotes() : null);
        int insertResult = baseMapper.insert(borrowing);
        if (insertResult < 1) throw new ObjectException("借阅记录保存失败，请重试");
        book.setAvailableCopies(book.getAvailableCopies() - 1);
        int updateResult = bookMapper.updateById(book);
        if (updateResult < 1) throw new ObjectException("更新图书库存失败，请重试");
    }

    /**
     * 用户归还书籍
     *
     * @param returnBookDto dto
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void returnBook(@NotNull ReturnBookDto returnBookDto) {

        Integer borrowingId = returnBookDto.getBorrowingId();
        if (borrowingId == null) {
            throw new ObjectException(MessageConstants.BORROWINGID_NULL);
        }
        Borrowings borrowing = baseMapper.selectById(borrowingId);
        if (borrowing == null) {
            throw new ObjectException("借阅记录不存在");
        }

        if (borrowing.getStatus().equals(StatusConstants.ZERO) && borrowing.getFine() != null &&
                borrowing.getFine().compareTo(BigDecimal.ZERO) > 0) {
            throw new ObjectException("该图书以逾期请先去支付" + borrowing.getFine() + "元");
        }


        if (!borrowing.getStatus().equals(StatusConstants.ONE)) { // 1 表示借阅中
            throw new ObjectException("该借阅记录已归还或状态异常");
        }


        borrowing.setStatus(StatusConstants.TWO);
        borrowing.setReturnDate(LocalDateTime.now());

        int updateBorrowingResult = baseMapper.updateById(borrowing);
        if (updateBorrowingResult < 1) {
            throw new ObjectException("更新借阅记录失败，请重试");
        }


        Books book = bookMapper.selectById(borrowing.getBookId());
        if (book == null) {
            throw new ObjectException("图书信息不存在");
        }


        book.setAvailableCopies(book.getAvailableCopies() + 1);
        int updateBookResult = bookMapper.updateById(book);
        if (updateBookResult < 1) {
            throw new ObjectException("更新图书库存失败，请重试");
        }
    }


    private LocalDateTime convertToStartOfDay(LocalDate date) {
        return date != null ? date.atStartOfDay() : null;
    }

    private LocalDateTime convertToEndOfDay(LocalDate date) {
        return date != null ? date.atTime(LocalTime.MAX) : null;
    }
}
